﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Diagnostics.Contracts;

namespace ShredTheLog.Controls
{
    public sealed class RadarPanel : Panel, IScrollInfo
    {
        private TranslateTransform _translateTransform = new TranslateTransform();

        public RadarPanel()
        {
            this.RenderTransform = _translateTransform;
        }

        protected override Size MeasureOverride(Size availableSize)
        {
            Size childSize = availableSize;
            foreach (UIElement child in InternalChildren)
            {
                child.Measure(childSize);
            }

            ExtentWidth = Math.Min(availableSize.Width, availableSize.Height);
            Contract.Assume(ExtentWidth >= 0);
            DefineSlots(availableSize.Width, ExtentWidth);
            int pageItemCount = _slots.Count;
            if (pageItemCount != 0)
                ExtentHeight = InternalChildren.Count / pageItemCount + ((InternalChildren.Count ^ pageItemCount) != 0 ? 1 : 0);
            else
                ExtentHeight = 1;
            Contract.Assume(ExtentHeight >= 0);

            _verticalOffset = 0;

            if (ScrollOwner != null)
                ScrollOwner.InvalidateScrollInfo();

            return new Size(ExtentWidth, ExtentHeight);
        }

        public double ItemRadius
        {
            get { return (double)GetValue(ItemRadiusProperty); }
            set { SetValue(ItemRadiusProperty, value); }
        }
        public static readonly DependencyProperty ItemRadiusProperty = DependencyProperty.Register("ItemRadius", typeof(double), typeof(RadarPanel), new UIPropertyMetadata(10.0));

        public double RadialPadding
        {
            get { return (double)GetValue(RadialPaddingProperty); }
            set { SetValue(RadialPaddingProperty, value); }
        }
        public static readonly DependencyProperty RadialPaddingProperty = DependencyProperty.Register("RadialPadding", typeof(double), typeof(RadarPanel), new UIPropertyMetadata(50.0));

        public static bool GetIsScanning(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsScanningProperty);
        }
        public static void SetIsScanning(DependencyObject obj, bool value)
        {
            obj.SetValue(IsScanningProperty, value);
        }
        public static readonly DependencyProperty IsScanningProperty = DependencyProperty.RegisterAttached("IsScanning", typeof(bool), typeof(RadarPanel), new UIPropertyMetadata(false));

        private List<Point> _slots;
        private double _pageHeight;

        private void DefineSlots(double width, double extentWidth)
        {
            _pageHeight = extentWidth;
            _slots = new List<Point>();
            double areaRadius = (extentWidth - RadialPadding) / 2.0;
            int circleCount = (int)(areaRadius / ItemRadius / 2.0);
            circleCount = circleCount > 0 ? circleCount : 1;
            for (int circleCnt = 0; circleCnt < circleCount; circleCnt++)
            {
                double r = areaRadius / circleCount * circleCnt + ItemRadius;
                double p = 2 * Math.PI * r;
                int circleItemCount = (int)(p / ItemRadius / 2.0);
                for (int cnt = 0; cnt < circleItemCount; cnt++)
                {
                    double a = 2 * Math.PI / circleItemCount * cnt;
                    double x = Math.Cos(a) * r + width / 2.0;
                    double y = Math.Sin(a) * r + _pageHeight / 2.0;
                    _slots.Add(new Point(x, y));
                }
            }
        }

        private static readonly Random Rnd = new Random();

        protected override Size ArrangeOverride(Size finalSize)
        {
            int page = 0;
            List<Point> pageSlots = new List<Point>(_slots);
            foreach (UIElement child in InternalChildren)
            {
                int slotIndex = Rnd.Next(pageSlots.Count);
                double x = pageSlots[slotIndex].X - child.DesiredSize.Width / 2.0;
                double y = pageSlots[slotIndex].Y + _pageHeight * page - child.DesiredSize.Height / 2.0;
                child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
                Contract.Assume(slotIndex < pageSlots.Count);
                pageSlots.RemoveAt(slotIndex);
                if (pageSlots.Count == 0)
                {
                    Contract.Assume(_slots != null);
                    pageSlots.AddRange(_slots);
                    page++;
                }
            }

            if (ScrollOwner != null)
                ScrollOwner.InvalidateScrollInfo();

            return finalSize; // Returns the final Arranged size
        }

        #region IScrollInfo Members
        public bool CanHorizontallyScroll { get; set; }
        public bool CanVerticallyScroll { get; set; }
        public double ExtentHeight { get; private set; }
        public double ExtentWidth { get; private set; }
        public double HorizontalOffset { get { return 0; } }
        public void LineDown() { }
        public void LineLeft() { }
        public void LineRight() { }
        public void LineUp() { }
        public Rect MakeVisible(Visual visual, Rect rectangle) { return Rect.Empty; }
        public void MouseWheelDown() { }
        public void MouseWheelLeft() { }
        public void MouseWheelRight() { }
        public void MouseWheelUp() { }
        public void PageDown() { VerticalOffset++; }
        public void PageLeft() { }
        public void PageRight() { }
        public void PageUp() { VerticalOffset--; }
        public ScrollViewer ScrollOwner { get; set; }
        public void SetHorizontalOffset(double offset) { }
        public void SetVerticalOffset(double offset) { VerticalOffset = 0; }

        private double _verticalOffset;
        public double VerticalOffset
        {
            get { return _verticalOffset; }
            private set
            {
                if (value < 0 || ViewportHeight >= ExtentHeight)
                    _verticalOffset = 0;
                else if (value + ViewportHeight >= ExtentHeight)
                    _verticalOffset = ExtentHeight - ViewportHeight;
                else
                    _verticalOffset = value;

                _translateTransform.Y = -VerticalOffset * _pageHeight;

                if (ScrollOwner != null)
                    ScrollOwner.InvalidateScrollInfo();
                //InvalidateArrange();
            }
        }

        public double ViewportHeight { get { return 1; } }
        public double ViewportWidth { get { return 1; } }
        #endregion
    }
}
